/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *         The kernel's entry, where most of the initialization work is done.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, 
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. 
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include "fs.h"
#include "irq.h"
#include "test.h"
#include "time.h"
#include "stdio.h"
#include "sched.h"
#include "screen.h"
#include "common.h"
#include "syscall.h"
#include "smp.h"
#include "mm.h"
#include "mac.h"
#include "mailbox.h"

#define TASK_INIT (00)

queue_t ready_queue;
queue_t block_queue;
queue_t sleeping_queue;

uint64_t exception_handlers[32];

static void init_memory()
{
    queue_init(&free_list);
    queue_init(&busy_list);
    // 置一下pagemask
    // divid physical address from 1G to 2G into page frames
    physical_frame_initial(0x20000000, 0x20004000);

    init_TLB();
}

static void init_pcb()
{
    int i, j, k;
    PID = 0;
    bzero(&pcb[0], 32 * sizeof(pcb[i]));
    for (i = 0; i < NUM_MAX_TASK; i++)
    {
        pcb[i].prev = NULL;
        pcb[i].next = NULL;
        pcb[i].status = TASK_EXITED;
        pcb[i].core = -1;
        pcb[i].sched_time = 0;
    }
    strcpy(pcb[0].name, task0.name);
    pcb[0].pid = PID;
    PID++;
    pcb[0].type = task0.type;
    pcb[0].kernel_state = 0;
    pcb[0].kernel_context.reg[29] = STACK_MIN_KERNEL;
    //pcb[0].user_context.reg[29] = STACK_MIN_USER;
    pcb[0].user_context.reg[29] = STACK_MIN_KERNEL + STACK_SIZE;
    pcb[0].kernel_context.reg[31] = (uint64_t)first_entry;
    pcb[0].kernel_context.cp0_status = 0x10008003;
    pcb[0].user_context.cp0_status = 0x10008003;
    pcb[0].user_context.cp0_epc = task0.entry_point;
    pcb[0].kernel_context.cp0_epc = task0.entry_point;

    pcb[0].priority = task0.priority;
    pcb[0].init_priority = task0.priority;

    queue_init(&ready_queue);
    queue_init(&block_queue);
    queue_init(&sleeping_queue);

    // init Lock array
    for (i = 0; i < LOCK_TOTAL_NUM; i++)
    {
        queue_init(&Lock[i].mutex_lock_queue);
        Lock[i].status = UNLOCKED;
        Lock[i].lock_id = i;
    }

    // init mailbox
    do_mbox_init();

    // fill pcb array
    for (j = 0; j < num_shell_tasks; j++)
    {
        strcpy(pcb[j + 1].name, shell_tasks[j]->name);
        pcb[j + 1].pid = PID;
        PID++;
        pcb[j + 1].entry_point = shell_tasks[j]->entry_point;
        pcb[j + 1].type = shell_tasks[j]->type;
        pcb[j + 1].status = TASK_READY;
        pcb[j + 1].kernel_state = 0;
        pcb[j + 1].priority = shell_tasks[j]->priority;
        pcb[j + 1].init_priority = shell_tasks[j]->priority;

        // give kernel_context an initial entry, change to user status
        pcb[j + 1].kernel_context.reg[31] = (uint64_t)first_entry;

        pcb[j + 1].kernel_context.cp0_status = 0x10008003;
        pcb[j + 1].user_context.cp0_status = 0x10008003;

        // pcb[j + 1].kernel_context.reg[29] = STACK_MIN_KERNEL + (j + 1) * STACK_SIZE;
        // pcb[j + 1].user_context.reg[29] = STACK_MIN_USER + (j + 1) * STACK_SIZE;
        pcb[j + 1].kernel_context.reg[29] = STACK_MIN_KERNEL;
        pcb[j + 1].user_context.reg[29] = STACK_MIN_KERNEL + (j + 1) * STACK_SIZE;

        pcb[j + 1].user_context.cp0_epc = shell_tasks[j]->entry_point;
        pcb[j + 1].kernel_context.cp0_epc = shell_tasks[j]->entry_point;

        // init lock[]
        for (k = 0; k < LOCK_TOTAL_NUM; k++)
        {
            pcb[j + 1].lock[k] = NULL;
        }
        pcb[j + 1].lock_num = 0;
        pcb[j + 1].core = 0;
        // push pcb into ready_queue
        queue_push(&ready_queue, &pcb[j + 1]);
        //init_page_table(&pcb[j + 1]);
    }
    // initial current_running pointer
    current_running[0] = &pcb[0];
    //current_running[1] = &pcb[0];
    core_id = get_cpu_id();

    printk("task0 addr: 0x%x\n", &drawing_task1);
    printk("task1 addr: 0x%x\n", &rw_task1);
}

static void init_exception_handler()
{
    int i = 0;
    for (i = 0; i < 32; i++)
    {
        exception_handlers[i] = (uint64_t)&handle_other;
    }
    exception_handlers[INT] = (uint64_t)&handle_int;
    exception_handlers[SYS] = (uint64_t)&handle_syscall;
    exception_handlers[TLBL] = (uint64_t)&handle_tlb;
    exception_handlers[TLBS] = (uint64_t)&handle_tlb;
    exception_handlers[MOD] = (uint64_t)&handle_tlb_modify;
}

static void init_exception()
{
    /* init cp0_status */
    /* close interrupt */
    uint64_t cp0_status = get_cp0_status();
    cp0_status = cp0_status & 0xfffffffe;
    set_cp0_status(cp0_status);

    /* set pointer array */
    init_exception_handler();

    /* move function to 0x80000180 */
    memcpy((void *)(BEV0_EBASE + BEV0_OFFSET), (void *)exception_handler_begin, exception_handler_end - exception_handler_begin);

    memcpy((void *)(BEV1_EBASE + BEV1_OFFSET), (void *)exception_handler_begin, exception_handler_end - exception_handler_begin);

    /* move tlb exception function entry to 0x80000000 */
    memcpy((void *)(BEV0_EBASE), (void *)TLBexception_handler_begin, TLBexception_handler_end - TLBexception_handler_begin);

    /* set COUNT & set COMPARE */
    reset_timer();
}

// [2]
// extern int read_shell_buff(char *buff);

static void init_syscall(void)
{
    int fn;
    // init all the pointer in syscall array
    for (fn = 0; fn < NUM_SYSCALLS; fn++)
    {
        syscall[fn] = (uint64_t(*)()) & invalid_syscall;
    }

    syscall[SYSCALL_MUTEX_LOCK_INIT] = (uint64_t(*)()) & do_mutex_lock_init;
    syscall[SYSCALL_MUTEX_LOCK_ACQUIRE] = (uint64_t(*)()) & do_mutex_lock_acquire;
    syscall[SYSCALL_MUTEX_LOCK_RELEASE] = (uint64_t(*)()) & do_mutex_lock_release;

    syscall[SYSCALL_SLEEP] = (uint64_t(*)()) & do_sleep;
    syscall[SYSCALL_GET_TIMER] = (uint64_t(*)()) & get_timer;
    syscall[SYSCALL_DO_SCHEDULER] = (uint64_t(*)()) & do_scheduler;
    syscall[SYSCALL_WRITE] = (uint64_t(*)()) & do_write;

    syscall[SYSCALL_PS] = (uint64_t(*)()) & do_process_show;
    syscall[SYSCALL_SPAWN] = (uint64_t(*)()) & do_spawn;
    syscall[SYSCALL_WAITPID] = (uint64_t(*)()) & do_waitpid;
    syscall[SYSCALL_KILL] = (uint64_t(*)()) & do_kill;
    syscall[SYSCALL_EXIT] = (uint64_t(*)()) & do_exit;

    syscall[SYSCALL_CURSOR] = (uint64_t(*)()) & screen_move_cursor;
    syscall[SYSCALL_SCREEN_CLEAR] = (uint64_t(*)()) & screen_clear;

    syscall[SYSCALL_BINSEMOP] = (uint64_t(*)()) & do_binsemop;
    syscall[SYSCALL_BINSEMGET] = (uint64_t(*)()) & do_binsemget;

    syscall[SYSCALL_CONDITION_INIT] = (uint64_t(*)()) & do_condition_init;
    syscall[SYSCALL_CONDITION_WAIT] = (uint64_t(*)()) & do_condition_wait;
    syscall[SYSCALL_CONDITION_SIGNAL] = (uint64_t(*)()) & do_condition_signal;
    syscall[SYSCALL_CONDITION_BROADCAST] = (uint64_t(*)()) & do_condition_broadcast;

    syscall[SYSCALL_SEMAPHORE_INIT] = (uint64_t(*)()) & do_semaphore_init;
    syscall[SYSCALL_SEMAPHORE_UP] = (uint64_t(*)()) & do_semaphore_up;
    syscall[SYSCALL_SEMAPHORE_DOWN] = (uint64_t(*)()) & do_semaphore_down;

    syscall[SYSCALL_BARRIER_INIT] = (uint64_t(*)()) & do_barrier_init;
    syscall[SYSCALL_BARRIER_WAIT] = (uint64_t(*)()) & do_barrier_wait;

    syscall[SYSCALL_GETPID] = (uint64_t(*)()) & do_getpid;
    syscall[SYSCALL_MBOX_INIT] = (uint64_t(*)()) & do_mbox_init;
    syscall[SYSCALL_MBOX_OPEN] = (uint64_t(*)()) & do_mbox_open;
    syscall[SYSCALL_MBOX_CLOSE] = (uint64_t(*)()) & do_mbox_close;
    syscall[SYSCALL_MBOX_SEND] = (uint64_t(*)()) & do_mbox_send;
    syscall[SYSCALL_MBOX_RECV] = (uint64_t(*)()) & do_mbox_recv;

    syscall[SYSCALL_TASK_SET] = (uint64_t(*)()) & do_task_set;
    syscall[SYSCALL_TASK_SET_PID] = (uint64_t(*)()) & do_task_set_pid;
    syscall[SYSCALL_GET_CPU_ID] = (uint64_t(*)()) & get_cpu_id;

    syscall[SYSCALL_SHMGET] = (uint64_t(*)()) & do_shmget;
    syscall[SYSCALL_SHMAT] = (uint64_t(*)()) & do_shmat;
    syscall[SYSCALL_SHMDT] = (uint64_t(*)()) & do_shmdt;

    syscall[SYSCALL_TLB] = (uint64_t(*)()) & get_TLB_info;
    syscall[SYSCALL_CHECK_PAGE_FRAME] = (uint64_t(*)()) & do_check_page_frame;
    syscall[SYSCALL_FREE_LIST] = (uint64_t(*)()) & do_free_list;
    syscall[SYSCALL_BUSY_LIST] = (uint64_t(*)()) & do_busy_list;
}

/* [0] The beginning of everything >_< */
void __attribute__((section(".entry_function"))) _start(void)
{
    char *msg = "\rHello OS!\n\r";
    void (*pprintstr)(char *str);
    pprintstr = (void *)0xffffffff8f0d5534;
    (*pprintstr)(msg);
    //asm_start();

    /* init stack space */
    //init_stack();
    //printk("\r> [INIT] Stack heap initialization succeeded.\n");

    /* init interrupt */
    init_exception();
    printk("\r> [INIT] Interrupt processing initialization succeeded.\n");

    init_memory();
    printk("\r> [INIT] Virtual memory initialization succeeded.\n");
    // init system call table (0_0)
    /* init system call table */

    init_syscall();
    printk("\r> [INIT] System call initialized successfully.\n");

    /* init Process Control Block */

    init_pcb();
    printk("\r> [INIT] PCB initialization succeeded.\n");

    /* init screen */
    init_screen();
    //printk("\r> [INIT] SCREEN initialization succeeded.\n");

    /* init filesystem */
    //read_super_block();

    /* wake up core1*/
    lockkey = 0;
    //loongson3_boot_secondary();

    /* set cp0_status register to allow interrupt */
    // enable exception and interrupt
    // ERL = 0, EXL = 0, IE = 1
    reset_timer();
    set_cp0_status(0x10008001);
    uint64_t count, compare;
    while (1)
    {
    };
    return;
}
